package edu.kufpg.armatus.util;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
/**
* A {@link Button} that "sticks" when the user clicks it. That is, this {@code Button}'s
* {@link OnClickListener} will be called once when the user initially clicks the button, and
* any subsequent clicks will have no effect until {@link #unstick()} is called.
*/
public class StickyButton extends Button {
/** The listener that is called upon an initial button click. */
private OnClickListener mOnClickListener;
/** If {@code StickyButton} is stuck, clicks will not call {@link View.OnClickListener
* OnClickListener}'s {@link View.OnClickListener#onClick(View) onClick(View)} method. */
private boolean mIsStuck = false;
/** Used to prevent the {@link Button} from queuing clicks, even if the {@code Button}
* is not enabled. */
private final ReentrantLock mLock = new ReentrantLock(true);
/** {@link #mLock}'s condition. */
private final Condition mLockInEffect = mLock.newCondition();
public StickyButton(Context context) {
super(context);
init();
}
public StickyButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public StickyButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
super.setOnClickListener(new OnClickListener() {
@Override
public final void onClick(View v) {
click(true);
}
});
}
@Override
public void setOnClickListener(OnClickListener l) {
mOnClickListener = l;
}
/**
* Returns whether a click will call this {@link Button}'s {@link View.OnClickListener
* OnClickListener}.
* @return {@code true} if the button will call {@link View.OnClickListener#onClick(View)
* onClick(View)}.
*/
public boolean isStuck() {
return mIsStuck;
}
/**
* Makes this {@link Button}'s {@link View.OnClickListener OnClickListener} react to the
* next click.
*/
public void unstick() {
mLock.lock();
try {
if (mIsStuck) {
mLockInEffect.signal();
mIsStuck = false;
setEnabled(true);
}
} finally {
mLock.unlock();
}
}
/**
* Prevents this {@link Button}'s {@link View.OnClickListener OnClickListener} from calling
* {@link View.OnClickListener#onClick(View) onClick(View)} upon any subsequent clicks until
* {@link #unstick()} is called. This method will also allow you to determine whether or not
* the {@code OnClickListener} should be called on the initial click.
* @param callListener if {@code true}, {@link #mOnClickListener} will call {@code onClick(View)}
* Setting this to {@code false} can be useful if you need to restore the {@link StickyButton}'s
* state (e.g., after a screen rotation).
*/
private void click(boolean callListener) {
mLock.lock();
try {
while (mIsStuck) {
try {
mLockInEffect.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mIsStuck = true;
setEnabled(false);
if (callListener && mOnClickListener != null) {
mOnClickListener.onClick(this);
}
} finally {
mLock.unlock();
}
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.isStuck = isStuck();
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
if (ss.isStuck) {
click(false);
}
}
protected static class SavedState extends BaseSavedState {
boolean isStuck;
SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(isStuck ? 1 : 0);
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
private SavedState(Parcel in) {
super(in);
isStuck = (in.readInt() != 0);
}
}
}